package com.blacksea.authserver.web.controller;

import com.blacksea.authserver.service.manager.AuthorizationManager;
import com.blacksea.authserver.service.manager.LoginManager;
import com.blacksea.authserver.service.manager.TokenManager;
import com.blacksea.authserver.service.model.MixTokenDTO;
import com.blacksea.authserver.web.request.AuthorizationCodeRequest;
import com.blacksea.authserver.web.request.LoginRequest;
import com.blacksea.authserver.web.request.TokenRequest;
import com.blacksea.authserver.web.request.VerifyRequest;
import com.blacksea.authserver.web.response.AuthorizationCodeResponse;
import com.blacksea.authserver.web.response.LoginResponse;
import com.blacksea.authserver.web.response.ResponseStateEnum;
import com.blacksea.authserver.web.response.TokenResponse;
import com.blacksea.authserver.web.response.VerifyResponse;

import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;

/**
 * AuthorizationController
 *
 * @author blacksea3(jxt) 2021/4/11 14:49
 */
@Controller
@RequestMapping("/oauth")
public class OAuth2Controller {

    @Resource(name = "authorizationManager")
    private AuthorizationManager authorizationManager;

    @Resource(name = "loginManager")
    private LoginManager loginManager;

    @Resource(name = "tokenManager")
    private TokenManager tokenManager;

    /**
     * 认证GET入口
     *
     * @param responseType .
     * @param clientId .
     * @param redirectUri .
     * @return .
     */
    // TODO: 无法解析参数的测试
    @GetMapping(value = "/authorize")
    public ModelAndView authorizationGET(
        @RequestParam("response_type") String responseType,
        @RequestParam("client_id") String clientId,
        @RequestParam("redirect_uri") String redirectUri) {
        AuthorizationCodeRequest authorizationCodeRequest = new AuthorizationCodeRequest(responseType, clientId,
            redirectUri);
        AuthorizationCodeResponse authorizationCodeResponse = this.authorizationPOST(authorizationCodeRequest);
        ModelAndView modelAndView = new ModelAndView();

        if (ResponseStateEnum.SUCCESS.getStr().equals(authorizationCodeResponse.getState())) {
            modelAndView.addObject("redirectUrl", authorizationCodeResponse.getRedirectUri());
            modelAndView.addObject("clientId", authorizationCodeRequest.getClientId());
            modelAndView.setViewName("login");
        } else {
            modelAndView.addObject("message", authorizationCodeResponse.getMessage());
            modelAndView.setViewName("error");
        }
        return modelAndView;
    }

    /**
     * 认证POST入口
     *
     * @param authorizationCodeRequest .
     * @return .
     */
    @ResponseBody
    @PostMapping(value = "/authorize", produces = "application/json;charset=UTF-8")
    public AuthorizationCodeResponse authorizationPOST(@RequestBody AuthorizationCodeRequest authorizationCodeRequest) {
        if ("code".equals(authorizationCodeRequest.getResponseType())) {
            String clientId = authorizationCodeRequest.getClientId();
            String redirectUri = authorizationCodeRequest.getRedirectUri();
            Pair<Boolean, String> res = authorizationManager.checkClientAndGenerateCode(clientId, redirectUri);
            if (res.getKey()) {
                return new AuthorizationCodeResponse(ResponseStateEnum.SUCCESS,
                    redirectUri, res.getValue(), "");
            } else {
                return new AuthorizationCodeResponse(ResponseStateEnum.FAIL,
                    "", "", res.getValue());
            }
        }
        return new AuthorizationCodeResponse(ResponseStateEnum.FAIL,
            "", "", "UNKNOWN RESPONSE TYPE");
    }

    /**
     * 登录POST 入口
     *
     * @param loginRequest .
     * @return .
     */
    @ResponseBody
    @PostMapping(value = "/login", produces = "application/json;charset=UTF-8")
    public LoginResponse loginPOST(@RequestBody LoginRequest loginRequest) {
        Pair<Boolean, String> res = loginManager.verify(loginRequest.getUsername(), loginRequest.getPassword());
        if (res.getLeft()) {
            return new LoginResponse(ResponseStateEnum.SUCCESS, "");
        } else {
            return new LoginResponse(ResponseStateEnum.FAIL, res.getRight());
        }
    }

    /**
     * token入口
     *
     * @param tokenRequest .
     * @return .
     */
    @ResponseBody
    @PostMapping(value = "/token", produces = "application/json;charset=UTF-8")
    public TokenResponse tokenPOST(@RequestBody TokenRequest tokenRequest) {
        switch (tokenRequest.getGrantType()) {
            case "authorization_code": {
                MixTokenDTO mixTokenDTO = tokenManager.generateToken(tokenRequest.getCode());
                if (mixTokenDTO.isSuccess()) {
                    // 当前只支持可刷新
                    return new TokenResponse(
                        mixTokenDTO.getAuthorizationToken(), mixTokenDTO.getTokenType(), mixTokenDTO.getExpireIn(),
                        mixTokenDTO.getRefreshToken(), ResponseStateEnum.SUCCESS, "");
                } else {
                    return new TokenResponse("", "", 0, "",
                        ResponseStateEnum.FAIL, mixTokenDTO.getMessage());
                }
            }
            case "refresh_token": {
                MixTokenDTO mixTokenDTO = tokenManager.refreshToken(tokenRequest.getCode());
                if (mixTokenDTO.isSuccess()) {
                    // 当前只支持可刷新
                    return new TokenResponse(
                        mixTokenDTO.getAuthorizationToken(), mixTokenDTO.getTokenType(), mixTokenDTO.getExpireIn(),
                        mixTokenDTO.getRefreshToken(), ResponseStateEnum.SUCCESS, "");
                } else {
                    return new TokenResponse("", "", 0, "",
                        ResponseStateEnum.FAIL, mixTokenDTO.getMessage());
                }
            }
            default: {
            }
        }
        return new TokenResponse("", "", 0, "",
            ResponseStateEnum.FAIL, "INVALID GRANT TYPE");
    }

    @ResponseBody
    @PostMapping(value = "/verify", produces = "application/json;charset=UTF-8")
    public VerifyResponse verifyPOST(@RequestBody VerifyRequest verifyRequest) {
        if (tokenManager.isValid(verifyRequest.getAccessToken())) {
            return new VerifyResponse(ResponseStateEnum.SUCCESS, "");
        } else {
            return new VerifyResponse(ResponseStateEnum.FAIL, "INVALID ACCESS TOKEN OR EXPIRED");
        }
    }
}
